package io.youngledo.usb.storage.impl;

import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.FileInfo;
import com.qiniu.storage.model.FileListing;
import com.qiniu.util.Auth;
import io.youngledo.usb.storage.*;
import io.youngledo.usb.exception.StorageConnectionException;
import io.youngledo.usb.exception.StorageOperationException;
import io.youngledo.usb.task.ProgressTracker;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.URL;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/// 七牛云Kodo存储服务适配器，区域：[地域和访问域名](https://developer.qiniu.com/kodo/1671/region-endpoint-fq)
///
public class QiniuStorageService extends AbstractObjectStorageService {

    private static final Logger logger = LoggerFactory.getLogger(QiniuStorageService.class);

    private Configuration qiniuConfig;
    private Auth auth;
    private BucketManager bucketManager;
    private UploadManager uploadManager;

    @Override
    public void connect(StorageConfig config) {
        if (config.type() != StorageType.QINIU_KODO) {
            throw new IllegalArgumentException("Invalid storage type for Qiniu Kodo service: " + config.type());
        }

        this.config = config;

        try {
            logger.info("Connecting to Qiniu Kodo");

            this.auth = Auth.create(config.accessKey(), config.secretKey());
            this.qiniuConfig = new Configuration(Region.autoRegion());
            this.bucketManager = new BucketManager(auth, qiniuConfig);
            this.uploadManager = new UploadManager(qiniuConfig);

            // 测试连接 - 尝试列出存储空间
            bucketManager.buckets();

            this.connected = true;
            logger.info("Successfully connected to Qiniu Kodo");
        } catch (Exception e) {
            logger.error("Failed to connect to Qiniu Kodo: {}", e.getMessage(), e);
            throw new StorageConnectionException("Failed to connect to Qiniu Kodo: " + e.getMessage(), e);
        }
    }

    @Override
    protected void performDisconnect() {
        // 七牛云SDK通常不需要显式断开连接，但我们清理引用
        auth = null;
        bucketManager = null;
        uploadManager = null;
        qiniuConfig = null;
        logger.info("Disconnected from Qiniu Kodo");
    }

    @SneakyThrows
    @Override
    public List<StorageBucket> listBuckets() {
        checkConnection();
        return Arrays.stream(bucketManager.buckets())
                .parallel().map(bucketName -> StorageBucket.of(bucketName, null))
                .toList();
    }

    @Override
    public void createBucket(String bucketName) {
        checkConnection();
        try {
            // 七牛云需要通过控制台或API创建存储空间，这里抛出不支持的操作异常
            // 因为七牛云的存储空间创建通常需要指定区域等参数
            throw StorageOperationException.operationFailed("Creating buckets programmatically is not supported by Qiniu Kodo. Please create buckets through the Qiniu console.");
        } catch (Exception e) {
            logger.error("Failed to create bucket {}: {}", bucketName, e.getMessage(), e);
            throw StorageOperationException.operationFailed("Failed to create bucket: " + e.getMessage(), e);
        }
    }

    @Override
    public void deleteBucket(String bucketName) {
        checkConnection();
        try {
            // 七牛云删除存储空间也需要通过控制台操作
            throw StorageOperationException.operationFailed("Deleting buckets programmatically is not supported by Qiniu Kodo. Please delete buckets through the Qiniu console.");
        } catch (Exception e) {
            logger.error("Failed to delete bucket {}: {}", bucketName, e.getMessage(), e);
            throw StorageOperationException.bucketNotFound(bucketName);
        }
    }

    @SneakyThrows
    @Override
    public List<StorageObject> listObjects(String bucketName, String prefix, boolean recursive) {
        checkConnection();
        String delimiter = recursive ? null : "/";
        FileListing fileListing = bucketManager.listFiles(bucketName, prefix, null, 1000, delimiter);
        List<StorageObject> objects = new ArrayList<>();

        // 处理文件对象
        if (fileListing.items != null) {
            objects.addAll(ObjectStorageHelper.processObjects(
                    List.of(fileListing.items),
                    prefix,
                    fileInfo -> fileInfo.key,
                    (fileInfo, key, displayName) -> {
                        LocalDateTime putTime = Instant.ofEpochSecond(fileInfo.putTime / 10000000)
                                .atZone(ZoneId.systemDefault())
                                .toLocalDateTime();

                        return new StorageObject(
                                key,
                                displayName,
                                fileInfo.fsize,
                                putTime,
                                false,
                                fileInfo.hash
                        );
                    }
            ));
        }

        // 处理目录（如果不是递归模式）
        if (!recursive && fileListing.commonPrefixes != null) {
            objects.addAll(ObjectStorageHelper.processCommonPrefixes(
                    List.of(fileListing.commonPrefixes),
                    prefix,
                    false  // 不移除末尾的斜杠
            ));
        }

        return objects;
    }

    @Override
    public void uploadFile(String bucketName, String objectKey, File file) {
        checkConnection();
        try {
            String upToken = auth.uploadToken(bucketName);
            Response response = uploadManager.put(file, objectKey, upToken);

            if (!response.isOK()) {
                throw new QiniuException(response);
            }

            logger.info("Successfully uploaded file {} to bucket {}", objectKey, bucketName);
        } catch (QiniuException e) {
            logger.error("Failed to upload file {} to bucket {}: {}", objectKey, bucketName, e.getMessage(), e);
            throw StorageOperationException.operationFailed("Failed to upload file: " + e.getMessage(), e);
        }
    }

    @Override
    public void uploadFile(String bucketName, String objectKey, InputStream inputStream, long contentLength) {
        checkConnection();
        try {
            String upToken = auth.uploadToken(bucketName);
            Response response = uploadManager.put(inputStream, objectKey, upToken, null, null);

            if (!response.isOK()) {
                throw new QiniuException(response);
            }

            logger.info("Successfully uploaded stream {} to bucket {}", objectKey, bucketName);
        } catch (QiniuException e) {
            logger.error("Failed to upload stream {} to bucket {}: {}", objectKey, bucketName, e.getMessage(), e);
            throw StorageOperationException.operationFailed("Failed to upload file: " + e.getMessage(), e);
        }
    }

    @SneakyThrows
    @Override
    public InputStream downloadFile(String bucketName, String objectKey) {
        checkConnection();
        // 七牛云下载需要构建下载URL
        // 由于这里需要域名信息，我们从配置中获取endpoint作为下载域名
        String domain = config.endpoint().replace("http://", "").replace("https://", "");
        String publicUrl = String.format("http://%s/%s", domain, objectKey);

        // 生成私有下载链接（假设是私有空间）
        String privateUrl = auth.privateDownloadUrl(publicUrl);

        // 使用Java标准库的HTTP客户端下载文件
        try (HttpClient httpClient = HttpClient.newHttpClient()) {
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(java.net.URI.create(privateUrl)).GET().build();
            HttpResponse<byte[]> response = httpClient.send(request,
                    HttpResponse.BodyHandlers.ofByteArray());
            if (response.statusCode() == 200) {
                return new ByteArrayInputStream(response.body());
            } else {
                throw StorageOperationException.objectNotFound(objectKey);
            }
        }
    }

    @Override
    public void downloadFile(String bucketName, String objectKey, File localFile) {
        checkConnection();
        try (InputStream inputStream = downloadFile(bucketName, objectKey);
             FileOutputStream outputStream = new FileOutputStream(localFile)) {
            inputStream.transferTo(outputStream);
        } catch (IOException e) {
            throw StorageOperationException.objectNotFound(objectKey);
        }
    }

    @Override
    public void deleteObject(String bucketName, String objectKey) {
        checkConnection();
        try {
            Response response = bucketManager.delete(bucketName, objectKey);

            if (!response.isOK()) {
                throw new QiniuException(response);
            }

            logger.info("Successfully deleted object {} from bucket {}", objectKey, bucketName);
        } catch (QiniuException e) {
            logger.error("Failed to delete object {} from bucket {}: {}", objectKey, bucketName, e.getMessage(), e);
            throw StorageOperationException.objectNotFound(objectKey);
        }
    }

    @Override
    public void deleteObjects(String bucketName, List<String> objectKeys) {
        checkConnection();
        try {
            if (objectKeys.isEmpty()) {
                return;
            }

            BucketManager.BatchOperations batchOperations = new BucketManager.BatchOperations();
            for (String key : objectKeys) {
                batchOperations.addDeleteOp(bucketName, key);
            }

            Response response = bucketManager.batch(batchOperations);

            if (!response.isOK()) {
                throw new QiniuException(response);
            }

            logger.info("Successfully deleted {} objects from bucket {}", objectKeys.size(), bucketName);
        } catch (QiniuException e) {
            logger.error("Failed to delete objects from bucket {}: {}", bucketName, e.getMessage(), e);
            throw StorageOperationException.operationFailed("Failed to delete objects: " + e.getMessage(), e);
        }
    }

    @Override
    public StorageObject getObjectInfo(String bucketName, String objectKey) {
        checkConnection();
        try {
            FileInfo fileInfo = bucketManager.stat(bucketName, objectKey);

            String displayName = objectKey;
            if (displayName.contains("/")) {
                displayName = displayName.substring(displayName.lastIndexOf('/') + 1);
            }

            LocalDateTime putTime = Instant.ofEpochSecond(fileInfo.putTime / 10000000)
                    .atZone(ZoneId.systemDefault())
                    .toLocalDateTime();

            return new StorageObject(
                    objectKey,
                    displayName,
                    fileInfo.fsize,
                    putTime,
                    false,
                    fileInfo.hash
            );
        } catch (QiniuException e) {
            logger.error("Failed to get object info for {} in bucket {}: {}", objectKey, bucketName, e.getMessage(), e);
            throw StorageOperationException.objectNotFound(objectKey);
        }
    }

    @Override
    public StorageType getStorageType() {
        return StorageType.QINIU_KODO;
    }

    @Override
    public String generatePresignedUrl(String bucketName, String objectKey, int expiry) {
        checkConnection();
        try {
            // 从配置中获取域名
            String domain = config.endpoint().replace("http://", "").replace("https://", "");
            String publicUrl = String.format("http://%s/%s", domain, objectKey);

            // 生成私有下载链接
            long deadline = System.currentTimeMillis() / 1000 + expiry;
            return auth.privateDownloadUrl(publicUrl, deadline);
        } catch (Exception e) {
            logger.error("Failed to generate presigned URL for {} in bucket {}: {}", objectKey, bucketName, e.getMessage(), e);
            throw StorageOperationException.operationFailed("Failed to generate presigned URL: " + e.getMessage(), e);
        }
    }

    @Override
    public void uploadFileWithProgress(String bucketName, String objectKey, File file, ProgressTracker progressTracker) {
        checkConnection();
        try {
            progressTracker.statusProperty().set("开始上传到七牛云Kodo...");

            // 七牛云SDK支持进度回调，这里简化实现
            uploadFile(bucketName, objectKey, file);

            progressTracker.complete();
        } catch (Exception e) {
            logger.error("Failed to upload file {} with progress: {}", objectKey, e.getMessage(), e);
            throw StorageOperationException.operationFailed("Failed to upload file with progress: " + e.getMessage(), e);
        }
    }

    @Override
    public void uploadFileWithProgress(String bucketName, String objectKey, InputStream inputStream, long contentLength, ProgressTracker progressTracker) {
        checkConnection();
        try {
            progressTracker.statusProperty().set("开始上传到七牛云Kodo...");

            uploadFile(bucketName, objectKey, inputStream, contentLength);

            progressTracker.complete();
        } catch (Exception e) {
            logger.error("Failed to upload stream {} with progress: {}", objectKey, e.getMessage(), e);
            throw StorageOperationException.operationFailed("Failed to upload file stream with progress: " + e.getMessage(), e);
        }
    }

}